#ifndef __M_BINDING_H__
#define __M_BINDING_H__
#include"../mqcommon/helper.hpp"
#include"../mqcommon/logger.hpp"
#include<unordered_map>
#include<memory>
#include<mutex>
#include<sstream>
#include<assert.h>

namespace mq{
    struct Binding
    {
        using ptr = std::shared_ptr<Binding>;
        std::string exchange_name;
        std::string queue_name;
        std::string binding_key;
        bool durable;

        Binding(){}
        Binding(const std::string& ecname,const std::string& qname,const std::string& key,bool dur):
        exchange_name(ecname),queue_name(qname),binding_key(key),durable(dur){}
    };
    using MsgQueueBindMap = std::unordered_map<std::string,Binding::ptr>;
    using BindingMap = std::unordered_map<std::string,MsgQueueBindMap>;

    class BindingMapper{
        public:
        BindingMapper(const std::string& filename):_sql_hander(filename){
            std::string path = Filehelper::parentDirectory(filename);
            Filehelper::createDirectory(path);
            assert(_sql_hander.open());
            createTable();
        }

        void createTable(){
            std::stringstream ss;
                ss<<"create table if not exists binding_table(";
                ss<<"exchange_name varchar(32),";
                ss<<"queue_name varchar(32),";
                ss<<"binding_key varchar(64),";
                ss<<"durable int);";
                int ret = _sql_hander.exec(ss.str(),nullptr,nullptr);
                if(ret == false){
                    LOG_ERRORM("cteate table binding false...");
                    abort();
                }
        }
        void deleteTable(){
                std::stringstream ss;
                ss<<"drop table if exists binding_table;";
                int ret = _sql_hander.exec(ss.str(),nullptr,nullptr);
                if(ret == false){
                    LOG_ERRORM("drop table binding false...");
                    abort();
                }
        }
        void insert(Binding::ptr bindptr){
                std::stringstream ss;
                ss<<"insert into binding_table values(";
                ss<<"'"<<bindptr->exchange_name<<"',";
                ss<<"'"<<bindptr->queue_name<<"',";
                ss<<"'"<<bindptr->binding_key<<"',";
                ss<<bindptr->durable<<");";
                int ret = _sql_hander.exec(ss.str(),nullptr,nullptr);
                //testing
                if(ret == false){
                    LOG_ERRORM("insert binding false...");
                }
        }
        void remove(const std::string& ename,const std::string& qname){
            std::stringstream ss;
            ss<<"delete from binding_table where exchange_name =";
            ss<<"'"<<ename<<"' and  queue_name = ";
            ss<<"'"<<qname<<"';";

            int ret = _sql_hander.exec(ss.str(),nullptr,nullptr);
            //testing
            if(ret == false){
                LOG_ERRORM("remove binding false...");
            }
        }
        void removeExchange(const std::string& ename){
            std::stringstream ss;
            ss<<"delete from binding_table where exchange_name =";
            ss<<"'"<<ename<<"';";
            int ret = _sql_hander.exec(ss.str(),nullptr,nullptr);
            //testing
            if(ret == false){
                LOG_ERRORM("removeExchange binding false...");

            }
        }
        void removeMsgQueue(const std::string& qname){
            std::stringstream ss;
            ss<<"delete from binding_table where queue_name =";
            ss<<"'"<<qname<<"';";
            int ret = _sql_hander.exec(ss.str(),nullptr,nullptr);
            //testing
            if(ret == false){
                LOG_ERRORM("removeMsgQueue binding false...");
            }
        }

        BindingMap recovery(){
            std::stringstream ss;
            ss<<"select exchange_name,queue_name,binding_key,durable from binding_table;";
            BindingMap  bmp;
            int ret = _sql_hander.exec(ss.str(),BindingCallback,&bmp);
            if(ret == false){
                LOG_ERRORM("recovery binding false...");
            }
            return bmp;
        }
        private:
            static int BindingCallback(void* arg,int col,char** row,char** field_name){
                BindingMap* bmpptr = static_cast<BindingMap*>(arg);
                Binding::ptr bptr = std::make_shared<Binding>();
                bptr->exchange_name = row[0];
                bptr->queue_name = row[1];
                bptr->binding_key = row[2];
                bptr->durable = std::stoi(row[3]);
                MsgQueueBindMap& msgbmp = (*bmpptr)[bptr->exchange_name];//注意+引用，才能真正插入
                msgbmp.insert(std::make_pair(bptr->queue_name,bptr));
                // bmpptr->insert(std::make_pair(bptr->exchange_name,msgbmp)); 这种办法出错，因为没有ename的话，就插入不了

                return 0;
            }
        private:
            SqliteHelper _sql_hander;
    };

    class BindingManager{
        public:
            using ptr = std::shared_ptr<BindingManager>;
            BindingManager(const std::string& filename):_bindMaper(filename){
                _bindings = _bindMaper.recovery();
            }

            bool bind(const std::string& ename,const std::string& qname,const std::string& key,bool durable){
                std::unique_lock<std::mutex> lock(_mutex);
                Binding::ptr bptr = std::make_shared<Binding>(ename,qname,key,durable);
                auto it = _bindings.find(ename);
                if((it != _bindings.end()) && (it->second.find(qname) != it->second.end())){
                    //已经存在了
                    return false;
                }
                if(durable) _bindMaper.insert(bptr);

                MsgQueueBindMap& mqbmp = _bindings[ename];
                mqbmp.insert(std::make_pair(qname,bptr));
                return true;
            }
            void unBind(const std::string& ename,const std::string& qname){
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _bindings.find(ename);
                if(it == _bindings.end()) return;
                
                if(it->second.find(qname) == it->second.end()) return;
                
                if(it->second.find(qname)->second->durable == true)
                    _bindMaper.remove(ename,qname);
                _bindings[ename].erase(qname);
            }
            
            bool removeExchangeBinding(const std::string& ename){
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _bindings.find(ename);
                if(it == _bindings.end()) return true;

                _bindMaper.removeExchange(ename);
                _bindings.erase(ename);
                return true;
            }
            bool removeMsgQueueBinding(const std::string& qname){
                std::unique_lock<std::mutex> lock(_mutex);
                _bindMaper.removeMsgQueue(qname);
                for(auto it = _bindings.begin();it != _bindings.end();++it){
                    it->second.erase(qname);
                }
                return true;
            }

            //test func...
            MsgQueueBindMap getExchangeBinding(const std::string& ename){
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _bindings.find(ename);
                if(it == _bindings.end()) return MsgQueueBindMap();
                return it->second;
            }

            Binding::ptr getMsgQueueBinding(const std::string& ename,const std::string& qname){
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _bindings.find(ename);
                    if(it == _bindings.end())return Binding::ptr();

                    auto msgqu = it->second.find(qname);
                    if(msgqu == it->second.end()) return Binding::ptr();
                    return msgqu->second;
            }
            bool exists(const std::string& ename,const std::string& qname){
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _bindings.find(ename);
                if(it == _bindings.end())return false;
                if(it->second.find(qname) == it->second.end()) return false;
                return true;
            }
            size_t size(){
                std::unique_lock<std::mutex> lock(_mutex);
                size_t bsize=0;
                for(auto it = _bindings.begin();it != _bindings.end();++it){
                    bsize += it->second.size();
                }
                return bsize;
            }

            void clear(){
                std::unique_lock<std::mutex> lock(_mutex);
                _bindings.clear();
                _bindMaper.deleteTable();
            }

        private:
            std::mutex _mutex;
            BindingMapper _bindMaper;
            BindingMap _bindings;
    };
}

#endif